package fudan.se.project.security.permisson;

import fudan.se.project.domain.User;
import fudan.se.project.service.infrastructure.base.ConferencePermissionService;
import fudan.se.project.service.infrastructure.base.PaperPermissionService;
import fudan.se.project.service.infrastructure.base.PostPermissionService;
import fudan.se.project.service.infrastructure.base.ReviewInfoPermissionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.Serializable;

/**
 * @author Gao Longchao
 */
@Slf4j
@Component
public class UserPermissionEvaluator implements PermissionEvaluator {


    private final ConferencePermissionService conferencePermissionService;
    private final PaperPermissionService paperPermissionService;
    private final ReviewInfoPermissionService reviewInfoPermissionService;
    private final PostPermissionService postPermissionService;

    @Autowired
    public UserPermissionEvaluator(ConferencePermissionService conferencePermissionService, PaperPermissionService paperPermissionService, ReviewInfoPermissionService reviewInfoPermissionService, PostPermissionService postPermissionService) {
        this.conferencePermissionService = conferencePermissionService;
        this.paperPermissionService = paperPermissionService;
        this.reviewInfoPermissionService = reviewInfoPermissionService;
        this.postPermissionService = postPermissionService;
    }

    /**
     * Checks the current user's permission for targetDomain.
     *
     * @param authentication     the current user.
     * @param targetDomainObject useless in this impl.
     * @param permission         the object describing required permission.
     *                           Its format: 'resourceName:resourceAuthority:resourceId'
     * @return true if has permission, false without permission.
     */
    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {

        String[] splits = processPermission(permission);

        if (splits.length == 3) {
            final String resourceType = splits[0].toUpperCase();
            final String authority = splits[1].toUpperCase();

            final long resourceId;
            try {
                resourceId = Long.parseLong(splits[2]);
            } catch (NumberFormatException e) {
                log.warn("number exception in permission evaluator");
                return false;
            }

            User user = (User) authentication.getPrincipal();

            switch (resourceType) {
                case CONFERENCE:
                    return conferencePermissionService.checkPermission(user, authority, resourceId);
                case PAPER:
                    return paperPermissionService.checkPermission(user, authority, resourceId);
                case REVIEW_INFO:
                    return reviewInfoPermissionService.checkPermission(user, authority, resourceId);
                case POST:
                    return postPermissionService.checkPermission(user, authority, resourceId);
                default:

            }
        }

        return false;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        return false;
    }

    private static String[] processPermission(Object permission) {

        if (permission instanceof String) {

            String permissionStr = (String) permission;
            permissionStr = StringUtils.trimAllWhitespace(permissionStr);

            String[] strings = permissionStr.split(":");

            if (strings.length == 3) return strings;
        }

        return new String[]{};
    }

    private static final String CONFERENCE = "CONFERENCE";
    private static final String PAPER = "PAPER";
    private static final String REVIEW_INFO = "REVIEWINFO";
    private static final String POST = "POST";
}
